package edu.lambdateacher;

/*
LambdaTeacher
Copyright (C) 2000  Kim Mason
Copyright (C) 2014  Vitalij Zadneprovskij

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

import java.io.Reader;
import java.io.StringReader;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import edu.lambdateacher.expressions.Expression;
import edu.lambdateacher.parser.LambdaParser;
import edu.lambdateacher.parser.LambdaScanner;
import edu.lambdateacher.parser.LambdaTokenType;
import edu.lambdateacher.parser.ParseException;
import edu.lambdateacher.parser.ParseResult;

public class InterpreterFacade {

	private static Logger log = Logger.getLogger(InterpreterFacade.class);

	private Map<String, Expression> boundExpressionMap = new HashMap<String, Expression>();

	/**
	 * This method parses the string that it is fed in, and sets the current
	 * expression to be equal to that. If the string is from a file, it writes
	 * the name of the binding (if it is a binding) to the textarea in
	 * LambdaTeacher. If the string is a binding, it adds that binding to the
	 * toplevel Environment.
	 * @throws ParseException 
	 */
	public InterpreterResponseBean enterExpression(Reader inreader, boolean fromfile) throws ParseException {

		log.debug("Begin of the enterExpression method. inreader = " + inreader + " fromfile = " + fromfile);

		if (inreader == null) {
			throw new IllegalArgumentException("inreader is null");
		}

		InterpreterResponseBean interpreterResponseBean = new InterpreterResponseBean();

		// scan the input stream
		LambdaScanner thescanner = new LambdaScanner(inreader);

		ParseResult parseResult = null;
		
		if (fromfile == true) {
			LambdaTokenType currentTokenType = thescanner.currentToken().getTokenType();
			
			while (currentTokenType != LambdaTokenType.EOF) {
				log.debug("Current token type: " + currentTokenType);
				LambdaParser theparser = new LambdaParser();
				// note that in Parse, toplevel may be modified
				parseResult = theparser.parse(thescanner, boundExpressionMap);

				currentTokenType = thescanner.currentToken().getTokenType();
			}
			interpreterResponseBean.setExpression(parseResult.getParseTree());
		} else {
			LambdaParser lambdaParser = new LambdaParser();
			parseResult = lambdaParser.parse(thescanner, boundExpressionMap);

			// if the parsed expression isn't null (there was no parse error)
			if (parseResult.getParseTree() != null) {
				interpreterResponseBean.setExpression(parseResult.getParseTree());
				interpreterResponseBean.setExpressionToDisplay(parseResult.getParseTree().stringRepresentation(false));
			} else
			// the input expression equals null
			{
				interpreterResponseBean.setExpression(null);
				interpreterResponseBean.setExpressionToDisplay("");
			}
		}

		return interpreterResponseBean;
	}
	
	public InterpreterResponseBean enterExpression(String expression) throws ParseException{
		Reader reader = new StringReader(expression);
		return enterExpression(reader,false);
	} 

	/***
	 * Reduces the current expression for at most maxReductions times
	 * @param expression the expression to be reduced
	 * @param maxReductions the maximum number of reductions to perform
	 * @return
	 */
	public ReductionResponseBean reduce(Expression expression, BigInteger maxReductions) {

		ReductionResponseBean reductionResponseBean = new ReductionResponseBean();

		if (expression == null) {
			throw new IllegalArgumentException("The expression is null!!");
		}

		if (PropertiesManager.expdlogon == true) {
			StringBuilder explain = new StringBuilder();
			if (expression.containsReducable()) {
				// explain normal reduction
				explain.append("You have chosen to completely normal reduce the current expression:\n\n");
				explain.append(expression.stringRepresentation(false) + "\n\n");
				explain.append("This will repeatedly select the leftmost outermost reducable expression, ");
				explain.append("and will reduce that expression.  This will continue until there are no ");
				explain.append("reducable expressions left.\n");
				explain.append("If it is possible to reduce an expression to normal form, normal reduction will ");
				explain.append("always achieve normal form.");
			} else {
				explain.append("The current expression can't be reduced, as it is already in normal form.");
			}

			reductionResponseBean.setExplanation(explain.toString());
		}

		// turn off explanation dialogs
		boolean oldvalue = PropertiesManager.expdlogon;
		PropertiesManager.expdlogon = false;
		
		List<String> intermediateExpressions= new ArrayList<String>();
		
		// reduce the expression for at most Properties.MAX_REDUCTIONS times
		BigInteger reductionsSoFar = BigInteger.ZERO;
		
		while (expression.containsReducable() && reductionsSoFar.compareTo(maxReductions) < 0 ) {
			reductionsSoFar = reductionsSoFar.add(BigInteger.ONE);
			expression = expression.normalReduce();
			if (expression == null){
				break;
			}else{
//				Expression newExpression = (Expression) expression.clone();
//				intermediateExpressions.add(newExpression.unParse(true));
			}
		}
		PropertiesManager.expdlogon = oldvalue;
		String lambdastring = null;
		if (expression != null) {
			lambdastring = expression.stringRepresentation(false);
		}
		if (lambdastring != null) {
			intermediateExpressions.add(lambdastring);
			reductionResponseBean.setLambdaString(lambdastring);
		} else {
			reductionResponseBean.setLambdaString("");
		}
		
		reductionResponseBean.setIntermediateExpressions(intermediateExpressions);

		return reductionResponseBean;

	}

}
